'''
client.py - Enable local code to access remote procedures hosted on Pillarbox.

TODO:description

Authors: Martin Norbury,
         Eric Saunders

October 2011
'''

import urllib,urllib2
import json

class _Client(object):
    def __init__(self, root='http://localhost:8000/'):
        self.root = root

    def __getattribute__(self,name):
        '''A bit of magic to allow callers to ask for e.g. client.square(2), and
           transparently receive the result of the remote procedure call.'''

        # Don't interfere with accessing attributes which actually exist
        if name in object.__getattribute__(self,'__dict__'):
            return object.__getattribute__(self,name)

        # User code wants client.module - hand it off for dispatch to the server
        else:
            self.remote_module = name
            return object.__getattribute__(self,'_message_dispatch')


    def _message_dispatch(self, *args, **kwargs):
        raise NotImplementedError('This method should be overriden by the subclass')



class _GetClient(_Client):
    def _message_dispatch(self, *args, **kwargs):
        # Construct the slash-delimited portion of the query string
        arg_string = '/'.join([ str(astring) for astring in args])

        # Construct the keyword-based part of the query string
        optional_args = '?'
        for key,value in kwargs.iteritems():
            optional_args += str(key) + '=' + str(value) + '&'

        # Construct and send the query string
        urlstring = self.root + self.remote_module + '/' + arg_string + optional_args
        url       = urllib2.urlopen(urlstring)

        # Deserialise and return the response
        return json.loads(url.readline())



class _PostClient(_Client):
    def _message_dispatch(self, *args, **kwargs):
        # JSON encode the argument payload
        data_dict  = dict(args=args, kwargs=kwargs)
        json_data  = json.dumps(data_dict)

        # Construct the POST request
        path_to_module = self.root + self.remote_module + '/'
        req = urllib2.Request(path_to_module,
                              data=urllib.urlencode({'data':json_data}))

        # Send the request
        url = urllib2.urlopen(req)

        # Deserialise and return the response
        return json.loads(url.readline())


def create_get_client():
    '''Factory for creating GET clients.'''
    return _GetClient()

def create_post_client():
    '''Factory for creating POST clients.'''
    return _PostClient()


if __name__ == '__main__':
    '''This is how you create and use clients to make remote calls.'''
    import sys

    client = create_get_client()
    result = client.square(2)
    print result

    client = create_post_client()
    result = client.square(2)
    print result
